#include "r61581_conf.h"
#include <stdio.h>

//#include "pico/time.h"
#include "pico/stdlib.h"
#include "pio_tft.h"
#include "tft_drv.h"
#include "time.h"

#ifdef USE_RP2040_RTROBOT    // 使用黑色小板

#define RST_PIN 11
#define DC_PIN  10
#define CS_PIN  12

#define WR_PIN 9
#define RD_PIN 8
#define D0_PIN 0
#else    // 使用官方 PICO

#define RST_PIN 11
#define DC_PIN  10
#define CS_PIN  12

#define WR_PIN 9
#define RD_PIN 8
#define D0_PIN 0
#endif

#define DC_0()  gpio_clr_mask(1ul << DC_PIN)
#define DC_1()  gpio_set_mask(1ul << DC_PIN)
#define CS_0()  gpio_clr_mask(1ul << CS_PIN)
#define CS_1()  gpio_set_mask(1ul << CS_PIN)
#define RST_0() gpio_clr_mask(1ul << RST_PIN)
#define RST_1() gpio_set_mask(1ul << RST_PIN)
#define RD_0()  gpio_clr_mask(1ul << RD_PIN)
#define RD_1()  gpio_set_mask(1ul << RD_PIN)

#define COLOR(r8, g8, b8) \
    ((uint16_t)(((b8) >> 3) & 0x1FU) << 11 | (uint16_t)(((g8) >> 2) & 0x3FU) << 5 | (uint16_t)(((r8) >> 3) & 0x1FU))

#define COLOR_SWAP(r8, g8, b8) \
    ((uint16_t)(((r8) >> 3) & 0x1FU) << 11 | (uint16_t)(((g8) >> 2) & 0x3FU) << 5 | (uint16_t)(((b8) >> 3) & 0x1FU))

static void r61581_set_tft_spec(void);

void write_command_byte(uint8_t c)
{
    // This also flushes any pending writes.
    tft_set_mode_single_byte();
    DC_0();
    tft_write(c);
    // Prepear for data bytes that will follow
    tft_flush();
    DC_1();
}

void _sleep_ms(uint32_t ms)
{
    volatile uint32_t i, j;

    for(i = 0; i < ms; i++)
    {
        for(j = 0; j < 5000; j++)
        {
        }
    }
}

void _init_gpio(void)
{
    // A mask with all gpio output pins we use.
    const uint kOutputMask =
        1ul << RST_PIN | 1ul << DC_PIN | 1ul << CS_PIN | 1ul << RD_PIN;

    gpio_init_mask(kOutputMask);

    RST_0();
    CS_1();
    DC_0();
    RD_1();

    gpio_set_dir_out_masked(kOutputMask);

    tft_init(D0_PIN, WR_PIN, 4);

    // Reset
    RST_0();
    _sleep_ms(20);
    RST_1();
    _sleep_ms(200);
}

uint32_t _write_reg(uint8_t cmd, uint8_t* p_buf, uint16_t len)
{
    uint32_t ret = 0;

    tft_set_mode_single_byte();

    DC_0();
    CS_0();

    tft_write(cmd);
    tft_flush();

    if(p_buf != NULL && len != 0)
    {
        DC_1();

        for(uint16_t i = 0; i < len; i++)
        {
            // write_data_byte(p_buf[i]);
            tft_write(p_buf[i]);
            // No need to flush. Ok to data bytes being queued.
            tft_flush();
        }
    }

    DC_0();
    CS_1();

    return ret;
}

void r61851_init(void)
{
    _init_gpio();

    // init reg
    r61581_set_tft_spec();
}

void r61581_fill(uint16_t color)
{
    DC_0();
    CS_0();

    write_command_byte(0x2c);

    DC_1();

    tft_set_mode_double_byte();
    multi_write_repeat(color, SCREEN_XSIZE * SCREEN_YSIZE);

    DC_0();
    CS_1();
}

void r61581_disp_pic(uint16_t* p_dat, uint32_t len)
{
    DC_0();
    CS_0();

    write_command_byte(0x2c);

    DC_1();

    tft_set_mode_double_byte();
    tft_multi_write_nbytes((uint16_t*)p_dat, len);

    DC_0();
    CS_1();
}



void r61581_set_area(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2)
{
    uint8_t width[4], height[4];
    width[0] = x1 >> 8;
    width[1] = x1 & 0xff;
    width[2] = x2 >> 8;
    width[3] = x2 & 0xff;

    height[0] = y1 >> 8;
    height[1] = y1 & 0xff;
    height[2] = y2 >> 8;
    height[3] = y2 & 0xff;

    _write_reg(cmd_2A[0], width, 4);
    _write_reg(cmd_2B[0], height, 4);
}


void r61581_disp_update(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t* p_buf)
{
    r61581_set_area(x1, y1, x2, y2);

    uint32_t len = (x2 - x1 + 1) * (y2 - y1 + 1);

    r61581_disp_pic(p_buf, len);
}

extern const unsigned char gImage_color[307200];

void r61581_test(void)
{
    r61851_init();

    while(1)
    {
        r61581_fill(COLOR(255, 0, 0));
        _sleep_ms(1000);

        r61581_fill(COLOR(0, 255, 0));
        _sleep_ms(1000);

        r61581_fill(COLOR(0, 0, 255));
        _sleep_ms(1000);

        r61581_disp_pic(gImage_color, sizeof(gImage_color) / 2);
        _sleep_ms(2000);
    }
}

static void r61581_set_tft_spec(void)
{
    _write_reg(cmd_B0[0], &cmd_B0[1], sizeof(cmd_B0) - 1);
    _write_reg(cmd_B3[0], &cmd_B3[1], sizeof(cmd_B3) - 1);
    // _write_reg(cmd_B4[0], &cmd_B4[1], sizeof(cmd_B4) - 1);
    _write_reg(cmd_C0[0], &cmd_C0[1], sizeof(cmd_C0) - 1);
    _write_reg(cmd_C1[0], &cmd_C1[1], sizeof(cmd_C1) - 1);
    _write_reg(cmd_C4[0], &cmd_C4[1], sizeof(cmd_C4) - 1);
    _write_reg(cmd_C6[0], &cmd_C6[1], sizeof(cmd_C6) - 1);
    _write_reg(cmd_C8[0], &cmd_C8[1], sizeof(cmd_C8) - 1);    // Gamma
    // _write_reg(cmd_36[0], &cmd_36[1], sizeof(cmd_36) - 1);
    // _write_reg(cmd_0C[0], &cmd_0C[1], sizeof(cmd_0C) - 1);
    _write_reg(cmd_3A[0], &cmd_3A[1], sizeof(cmd_3A) - 1);
    _write_reg(cmd_38[0], &cmd_38[1], sizeof(cmd_38) - 1);
    _write_reg(cmd_D0[0], &cmd_D0[1], sizeof(cmd_D0) - 1);
    _write_reg(cmd_D1[0], &cmd_D1[1], sizeof(cmd_D1) - 1);
    _write_reg(cmd_D2[0], &cmd_D2[1], sizeof(cmd_D2) - 1);

    // _write_reg(0x21, NULL, 0); // Invert mode

    _write_reg(cmd_11[0], &cmd_11[1], sizeof(cmd_11) - 1);

    _sleep_ms(150);

    _write_reg(cmd_2A[0], &cmd_2A[1], sizeof(cmd_2A) - 1);
    _write_reg(cmd_2B[0], &cmd_2B[1], sizeof(cmd_2B) - 1);

    _sleep_ms(10);

    _write_reg(cmd_29[0], &cmd_29[1], sizeof(cmd_29) - 1);

    _sleep_ms(100);

    _write_reg(cmd_2C[0], &cmd_2C[1], sizeof(cmd_2C) - 1);
    _sleep_ms(5);
}